{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5分钟学会Python怎样操作redis库\n",
    "\n",
    "***Redis简介：***   \n",
    "Redis是一个开源的使用C语言编写、支持网络访问、可基于内存亦可持久化的日志型、Key-Value数据库，并提供多种语言的API。\n",
    "\n",
    "***Redis支持多种数据结构：***  \n",
    "* String: 字符串\n",
    "* Hash: 散列\n",
    "* List: 列表\n",
    "* Set: 集合\n",
    "* Sorted Set: 有序集合\n",
    "\n",
    "***使用场景：***\n",
    "* 完全可以作为唯一的数据存储库，不需要MySQL\n",
    "* 如果搭配使用，因为性能比MySQL快，常常作为Web应用服务的缓存层，先访问Redis，访问不到再访问MySQL，同时更新到Redis\n",
    "\n",
    "\n",
    "***演示场景，一个文章展示的网站怎样存储数据：***  \n",
    "1. 使用Hash存储文章的（ID、标题）数据；\n",
    "2. 使用String存储每个文章的访问次数，可以每次加1计数（或者存储文章ID的标签、作者等附加属性都可以）；\n",
    "3. 使用List存储每个用户的访问文章的历史，按顺序记录；\n",
    "4. 使用Set存储访问网站的所有用户ID的集合；\n",
    "5. 使用Sorted Set存储网站的热榜，排序分数就是权重；"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用pip install redis可以安装"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import redis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "redis_conn = redis.Redis(host='192.168.0.119', port=6379)   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 清理所有现在的数据，方便测试\n",
    "for key in redis_conn.keys():\n",
    "    redis_conn.delete(key)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. 给网站新增几篇文章"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 使用hash，类似map的形式，存储（Id、标题）数据\n",
    "for idx in range(101, 106):\n",
    "    redis_conn.hset(\"articles\", str(idx), f\"this is {idx} article title\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. 给用户展示文章列表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "##############################\n",
      "b'101' b'this is 101 article title'\n",
      "101 this is 101 article title\n",
      "##############################\n",
      "b'102' b'this is 102 article title'\n",
      "102 this is 102 article title\n",
      "##############################\n",
      "b'103' b'this is 103 article title'\n",
      "103 this is 103 article title\n",
      "##############################\n",
      "b'104' b'this is 104 article title'\n",
      "104 this is 104 article title\n",
      "##############################\n",
      "b'105' b'this is 105 article title'\n",
      "105 this is 105 article title\n"
     ]
    }
   ],
   "source": [
    "# 展示所有的文章列表\n",
    "for article_id, article_title in redis_conn.hgetall(\"articles\").items():\n",
    "    # 分隔符\n",
    "    print(\"#\"*30)\n",
    "    # 默认返回bytes类型\n",
    "    print(article_id, article_title)\n",
    "    # 转换成str类型\n",
    "    print(article_id.decode(\"utf-8\"), article_title.decode(\"utf-8\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'this is 105 article title'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 展示单个文章的标题\n",
    "redis_conn.hget(\"articles\", \"105\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. 用户访问文章则产生行为记录"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def user_visit(uid, article_id):\n",
    "    \"\"\"产生了行为：uid访问了article_id\"\"\"\n",
    "    # 1. String结构，文章的访问次数加1\n",
    "    redis_conn.incr(f\"article_counter_{article_id}\")\n",
    "    # 2. List结构，记录uid的访问列表\n",
    "    redis_conn.lpush(f\"user_visit_{uid}\", str(article_id))\n",
    "    # 3. Set结构，记录uid的全站集合\n",
    "    redis_conn.sadd(f\"all_visit_uids\", str(uid))\n",
    "    # 4. SortedSet结构，文章的热度加1\n",
    "    redis_conn.zincrby(\"article_hots\", 1, str(article_id))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 模拟3个用户的访问记录\n",
    "user_visit(\"uid_01\", \"101\")\n",
    "user_visit(\"uid_01\", \"102\")\n",
    "user_visit(\"uid_01\", \"103\")\n",
    "\n",
    "user_visit(\"uid_02\", \"102\")\n",
    "user_visit(\"uid_02\", \"103\")\n",
    "user_visit(\"uid_02\", \"104\")\n",
    "\n",
    "user_visit(\"uid_03\", \"103\")\n",
    "user_visit(\"uid_03\", \"104\")\n",
    "user_visit(\"uid_03\", \"105\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. 查询文章的访问次数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'2'"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "redis_conn.get(f\"article_counter_104\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "b'1'"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "redis_conn.get(f\"article_counter_105\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. 展示一个用户的访问历史"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[b'103', b'102', b'101']"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "redis_conn.lrange(\"user_visit_uid_01\", 0, -1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[b'105', b'104', b'103']"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "redis_conn.lrange(\"user_visit_uid_03\", 0, -1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. 展示访问全站的用户集合"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{b'uid_01', b'uid_02', b'uid_03'}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "redis_conn.smembers(\"all_visit_uids\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. 展示文章热榜以及热度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(b'103', 3.0), (b'104', 2.0), (b'102', 2.0), (b'105', 1.0), (b'101', 1.0)]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "redis_conn.zrange(\"article_hots\", 0, -1, withscores=True, desc=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
